CONTENTS |
This appendix briefly summarizes some of the differences between Python and C++ classes. Python's class system can be thought of as a subset of C++'s. Although the comparison to Modula 3 may be closer, C++ is the dominant OOP language today. But in Python, things are intentionally simpler -- classes are simply objects with attached attributes that may have links to other class objects. They support generation of multiple instances, customization by attribute inheritance, and operator overloading, but the object model in Python is comparatively uncluttered. Here are some specific differences between Python and C++:
There is no real distinction between data members and methods in Python; both simply designate named attributes of instances or classes, bound to functions or other kinds of objects. Attributes are names attached to objects, and accessed by qualification: object.attribute. Methods are merely class attributes assigned to functions normally created with nested def statements; members are just attribute names assigned to other kinds of objects.
Class statements create class objects and assign them to a name. Statements that assign names within a class statement generate class attributes, and classes inherit attributes from all other classes listed in their class statement header line (multiple inheritance is supported; this is discussed in a moment).
Calling a class object as though it were a function generates a new class instance object. An instance begins with an empty namespace that inherits names in the class's namespace; assignments to instance attributes (e.g., to self attributes within class method functions) create attributes in the instance.
Both classes and instances (and any data they embed) are automatically reclaimed when no longer held. There is no new (classes are called instead) and Python's del statement removes just one reference, unlike C++'s delete.
Class and instance attributes, like simple variables, spring into existence when assigned, are not declared ahead of time, and may reference any type of object (they may even reference different object datatypes at different times).
Python inheritance is generally kicked off to search for an attribute name's value: given an expression of the form object.attribute, Python searches the namespace object tree at object and above for the first appearance of name attribute. An inheritance search also occurs when expression operators and type operations are applied to objects. A new, independent inheritance search is performed for every object.attribute expression that is evaluated -- even self.attr expressions within a method function search anew for attr at the instance object referenced by self and above.
Python classes are objects in memory at runtime -- they can be passed around a program to provide a sort of runtime type resource (e.g., a single function can generate instances of arbitrary classes passed in as an argument). Both class and instance objects carry interpreter information (e.g., a __dict__ attribute dictionary), and Python's type function allows object type testing. Instance objects' __class__ attributes reference the class they were created from, and class objects' __bases_ _ attributes give class superclasses (base classes).
Python's equivalent of the C++ this instance pointer is the first argument added to method function calls (and usually called self by convention). It is usually implicit in a call but is used explicitly in methods: there is no hidden instance scope for unqualified names. Python methods are just functions nested in a class statement that receive the implied instance objects in their leftmost parameters.
In Python, all methods and data members are virtual in the C++ sense: there is no notion of a compile-time resolution of attributes based on an object's type. Every attribute qualification (object.name) is resolved at runtime, based on the qualified object's type.
Methods called by a superclass but not defined by it correspond to C++'s concept of "pure virtual" methods: methods that must be redefined in a subclass. Since Python is not statically compiled, there is no need for C++'s special syntax to declare this case. Calls to undefined methods raise a name error exception at runtime, which may or may not be caught with try statements.
There is no static class data declaration; instead, assignments nested in a class statement generate attribute names associated with the class and shared by all its instances.
There is no notion of true access restrictions for attributes; every member and method is public in the C++ sense. Attribute hiding is a matter of convention rather than syntax: C++'s public, private, and protected constraints don't apply (but see also the new __X class name localization feature in Appendix A).
Objects may be immutable, but names are not -- there is no equivalent of C++'s const modifier. Nothing prevents a name or object from being changed in a method, and methods can change mutable arguments (the self object, for example). Convention and common sense replaces extra syntax.
There is no direct analogue for C++'s reference parameters. Python methods may return multiple values in a tuple and can change passed-in objects if they're mutable (for instance, by assigning to an object's attributes or changing lists and dictionaries in place). But there is no aliasing between names at the call and names in a function header: arguments are passed by assignment, which creates shared object references.
Special method names overload operators: there is no operator+ -like syntax but the effects are similar. For instance, a class attribute named __add__ overloads (intercepts and implements) application of the + operator to instances of the class; __getattr__ is roughly like C++ -> overloading. Arbitrary expressions require coding right-side methods (e.g., _ _radd__ ).
Python is dynamically typed -- names are references to arbitrary objects, and there is no notion of type declarations. C++ templates are neither applicable nor necessary. Python classes and functions can generally be applied to any object type that implements the interface protocols (operations and operators) expected by the class's code. Subjects need not be of a certain datatype.
Everything is friendly in Python. Because there is no notion of privacy constraints, any class can access the internals of another.
Python polymorphism is based on virtual method calls: the type of a qualified object determines what its methods do. Since Python arguments' types are never declared (dynamically typed), there is nothing like C++'s function overloading for dispatching to different versions of a function based on the datatypes of its arguments. You can explicitly test types and argument list lengths in methods instead of writing separate functions for each type combination (see type built-in function and *args function argument form).
Multiple inheritance is coded by listing more than one superclass in parentheses in a class statement header line. When multiple inheritance is used, Python simply uses the first appearance of an attribute found during a depth-first, left-to-right search through the superclass tree. Python resolves multiple inheritance conflicts this way instead of treating them as errors.
C++'s notion of virtual base classes doesn't quite apply in Python. A Python class instance is a single namespace dictionary (with a class pointer for access to inherited attributes). Classes add attributes to the class instance dictionary by assignment. Because of this structure, each attribute exists in just one place -- the instance dictionary. For inherited class attributes, the search of the superclass tree resolves references unambiguously.
Python only runs the one _ _init__ method found by the inheritance object tree search. It doesn't run all accessible classes' constructors automatically; if needed, we have to call other class constructors manually. But this is no harder than specifying superclass constructor arguments in C++. Python destructors ( __del__ ) run when an instance is garbage-collected (i.e., deallocated), not in response to delete calls.
C++ scope operators of the form Superclass::Method are used to extend inherited methods and disambiguate inheritance conflicts. Python's closest equivalent is Superclass.Method, a class object qualification. It isn't required for inheritance conflict resolution, but can be used to override the default search rule and to call back to superclasses in method extensions.
Instead of special syntax, Python method references are objects ; they may be passed, stored in data structures, and so on. Method objects come in two flavors: bound methods (when an instance is known) are instance/method pairs called later like simple functions, and unbound methods are simply references to a method function object and require an instance to be passed explicitly when called.
Naturally, Python has additional class features not found in C++, such as metaclass protocols : __setattr_ _ can be used to implement alternative interfaces, and an instance's __class__ pointer can be reset to change the class type of an object dynamically. Moreover, class attributes can be modified arbitrarily at runtime; classes are merely objects with attached attribute names.
In addition, Python differs from C++ in numerous ways besides its class model. For instance, there are neither type declarations nor compile and linking steps in Python; you cannot overload = in Python as you can in C++ (assignment isn't an operator in Python); and pointers, central to much C and C++ programming, are completely absent in Python (though object references can have some of the same effects). Instead of pointers, Python programs use first-class objects, which are automatically allocated and reclaimed.
Most of these differences stem from the fact that Python was designed for speed of development, not speed of execution; much of C++'s extra syntax would interfere with Python's purpose. See the O'Reilly text Learning Python for a complete introduction to Python classes and the remainder of the core Python language.
CONTENTS |